歡迎來到 Day26~!國慶假期的第一天!我們昨天更新了我們的題庫已經在 Supabase 的知識庫,同時解釋了為什麼我們暫時不打算弄真正的題庫資料庫。經過前面幾天的努力,我們的 AI 前端面試官已經漸趨完整,具備了核心功能:使用者系統、RAG 檢索、Judge0 客觀評測、Streaming 回覆,以及練習紀錄的回顧功能。當然,若要作為一個要上線的產品我們還有非常多可以努力的方向,我們在未來的兩天會試著再加入一個核心的挑戰讓整個應用更實用,但在那之前我們要稍微談一些理論面的東西,也就是當一個 Side Project 真正要上線前我們需要考慮的事情,在過去的文章中其實我們或多或少都已經有稍微帶到這個議題了,比方說 Gemini & Judge0 的成本管理、 Supabse 的用量、整體服務的可靠性等,不過那些資訊有些零散,在最後一波衝刺前我想稍微再談談這些概念。
這其實是個非常大的主題,仰賴著大量的實務經驗,這部分的控管實際上我在之前的工作中也比較少接觸到,因此對我來說也是個有趣的探討,只是礙於篇幅跟我個人經驗的限制(這玩意硬要講的話都可以在寫一系列的鐵人賽了),今天也只能算是摸摸皮毛而已,不過我想作為入門會是個不錯的起點,我們馬上開始吧!
我們的應用程式主要有兩項外部服務會產生費用:Gemini API (按 Token 計費) 和 Judge0 (按請求計費),我們這個段落會說明大概的概念以及我們目前在專案中做了哪些相關的舉措。
Gemini 的計費模型依賴於處理的 Token 數量,通常輸出 (Output) Token 比輸入 (Input) Token 貴,每個模型在收費時的標準也不盡相同,以Gemini為例,可以參考我們之前在第一週提出的計價頁面,雖然如果你是跟著我操作的免費仔的話,暫時你還不需要擔心這個問題,但若是產品真正要上線,理解這部分的成本管控就是必然的行為。
大致上來說我們在 Gemini 的使用分為三大部分,一般文本輸入、輸出以及向量化,我們在開發的過程中已經有做了基本的處理確保我們不會太浪費免費的額度,做最基礎的成本控制,如下表的整理:
費用來源 | 說明 | 成本控制策略 |
---|---|---|
輸入 Token | 我們的 Prompt、RAG Context、對話歷史。 | 極簡 Prompt:保持 Prompt 簡潔;精準 RAG:將相似度門檻 (match_threshold) 設定高一些,確保只傳送最相關的 keyPoints ,避免不必要的上下文佔用 Token。 |
輸出 Token | AI 的回饋 (JSON 評估結果、總結)。 | 強制 JSON Schema:確保 AI 輸出格式精確且不囉嗦。強制定義的 score、pros、cons 結構,就是在控制輸出的長度。 |
Embedding | 每次執行 seed-vector.js 或 RAG 檢索時,用於生成向量。 |
使用專用模型:我們採用 gemini-embedding-001 ,它的設計就是為了 Embedding 任務,效率高且成本低。 |
成本的估算實際上相當複雜,我們通常也只能假設大致的情境並說明這樣大致上是否可以負擔,這也是許多系統面試會接觸到的環節,也是最仰賴經驗的地方,除了多加練習跟實務操作之外別無他法,但我們還是可以試試看!就以一個實際場景來估算:
場景:一位使用者完整練習一題 React 概念題
Gemini 免費層限制:
以我們目前的實作:
換句話說,免費層足夠支撐約 300+ 次完整練習,對個人學習綽綽有餘。
但如果是 10 個使用者同時使用:
補充說明:強制的用量限制設計
目前市面上的模型多半都有提供限額或是用量警告的功能,若是你真的有串接實際的 llm api,務必在你的專案頁面針對該專案做類似的設置,確保你不會因為過量的非預期服務調用讓你信用卡帳單跟台積電一樣不斷衝高。
Judge0 服務按程式碼的執行次數計費。這部分很客觀,但成本可能比想像中高。
任何雲端服務都有「速率限制 (Rate Limiting)」,通常以每分鐘請求數 (RPM) 或每分鐘 Token 數 (TPM) 來衡量。一旦超過,API 會回傳 429 Too Many Requests 錯誤。
/api/interview/evaluate
處理函式中,如果我們遇到 Judge0 或 Gemini 的 5xx 錯誤或 429 錯誤,我們使用了 指數退避 (Exponential Backoff) 策略,也就是我們的retryAsyncFunction
函數,自動進行幾次重試,且每次重試的間隔時間加倍。grounded_evidence
無法取得,僅根據使用者文字和 RAG 進行語意評估。在 Day 24,我們為 practice_records
設定了 SELECT 的 RLS 策略,確保使用者只能讀取自己的紀錄,通常這樣的檢查也會在前/後端同時進行,資料庫這樣的策略便會是你的應用程式最後一層的防護,避免一些漏網之魚或是非預期的操作。
一般來說成熟的軟體大致上都分為幾層的防護,普遍的認知最佳實務是「前後端 + 資料庫三層檢查」:
想像一個攻擊場景:
/api/practice/history
user_id
: "別人的 UUID"雖然這稱不上什麼攻擊,不過只要想像一下同樣的情境變為寫入或修改資訊大致上就知道嚴重性了。
當然,你能限定的 Policy 自然也遠遠不止於 SELECT,舉個例子來說,一個完整的安全策略還需要考慮寫入 (Write)。我們必須確保當使用者提交答案時,他們不能偽造 user_id
來將紀錄寫入別人的名下。
這時候你就可以在主控台或是透過直接的 SQL 指令去限制這樣的行為,需要限制怎麼樣的操作、又需要限制怎麼樣的使用者就全依賴你對於你專案的了解,最基礎的當然就是在個人紀錄相關的資料只允許自己的讀取和寫入,其餘的部分就仰賴商業邏輯的設計。
今天的內容應該是整個系列文最短最短的一篇了,我們簡單探討了一些我們在目前專案中做的成本控管與可靠性的設計,雖然都相當的基礎,但足以讓你作為起點往後延伸,之後在開發時可以試著在過程中去思考這些問題,讓你之後的思維變得更為全面一些,大致回顧一下我們今天的內容
✅ 理解成本結構:分析了 Gemini (Token) 和 Judge0 (Request) 的計費模型,並確定了 RAG 和 Prompt 的節約策略。
✅ 確認容錯機制:概念性複習了處理速率限制的必要性,以及應用的降級策略。
✅ 複習RSL資料庫安全防護:了解我們對 practice_records
資料表的保護機制,以及還有什麼可能的額外防護。
了解了目前我們在專案功能之外做的一些設計後,明天開始我們就要回到最後的功能衝刺,我們會嘗試在我們的程式實作題目中加入真正的前端測試問題,例如 React & CSS,這實際上是個不小的項目,完全可以獨立成一個全新的主題,我會試著不使用 Docker 之類的獨立環境,用較低成本的實現方式讓你了解整個概念是怎麼實作的,未來若是真的有碰到類似的需求需要用完整的方案,這會是一個很好的切入點。